Date: 21/04/16
Author: Jonathan Chan
Description: Data Analysis on Retail Data
Initial Data Loading
library("tidyverse")
library("lubridate")
library("ggmap")
library("plotly")
library("viridis")
library("wesanderson")
library("DT")
Initialising data, which includes original data and new data, joining them to create a master set.
info <- read.table("./Data/All_info.txt", header = TRUE, na.strings = c("", "NA"),
stringsAsFactors = FALSE)
info_new <- read.csv("./Data/All_info(new)-1.csv", stringsAsFactors = FALSE, header = TRUE,
na.strings = c("", "NA"))
colnames(info_new) <- colnames(info)
info_temp <- rbind(info, info_new)
info_walk <- read.csv("./Data/info walk-1.csv", stringsAsFactors = FALSE)
Overall there are 6429 rows of data. With 1173 unique customers.
Converts the dates in character format to date format and calculate age from date of birth. Redacted a lot of columns
info_merge <- info_temp
info_merge$Order.date <- dmy(info_temp$Order.date)
info_merge$D.O.B. <- dmy(info_temp$D.O.B.)
info_merge <- info_merge %>% mutate(Age = year(as.period(interval(info_merge$D.O.B., "21/04/16"))))
select(info_merge[45:54, ], -c(Name, Post, D.O.B., Age, Sex, Gross.profit, Amount.1, Value, Order.value, Net.Order.Value, Proj..Value.w.o.discount, Proj..Cost.of.sales, Amount))
Category Analysis
To start of with some data cleansing to merge different type of horn products into a single group.
info_merge$Material <- gsub(pattern = "Buffalo Horn", replace = "Horn", x = info_merge$Material)
info_merge$Material <- gsub(pattern = "Carbon Horn", replace = "Horn", x = info_merge$Material)
info_merge$Material <- gsub(pattern = "Thin Horn", replace = "Horn", x = info_merge$Material)
By Material
Material with Age
material_plot <- ggplot(filter(info_merge, Age > 0 & Age < 100,
Material %in% c("Acetate", "Horn", "Combination", "Titanium", "Rimless")),
aes(x = Material, y = Age, fill = Material)) +
theme(axis.text.y=element_blank()) +
geom_boxplot() +
geom_jitter(alpha = 0.2) +
scale_fill_viridis(discrete=T) +
geom_violin(alpha = 0.25)
ggtitle("Frame Material with Age")
$`title`
[1] "Frame Material with Age"
$subtitle
NULL
attr(,"class")
[1] "labels"
# ggplotly(material_plot, width = 900)
material_plot

mat.type <- data.frame()
mat.type <- info_merge %>%
filter(Product.type %in% c("Frames", "Sunglasses"),
Material %in% c("Acetate", "Horn", "Combination", "Titanium", "Rimless"),
Amount > 0, # takes only frames made by TD
is.na(Sex) == FALSE) %>%
group_by(Product.type, Material, Sex) %>%
summarise(orders = n(), Total.Order.value = sum(Amount, na.rm = TRUE), no.customers = n_distinct(Name))
mat.type <- mutate(mat.type,
Percentage.Orders = orders/sum(mat.type$orders), # Creates profit/order and % of orders
Percentage.Order.Value = Total.Order.value/sum(mat.type$Total.Order.value),
Average.Order.value = Total.Order.value/orders,
Order.customer.ratio = orders/no.customers)
Lens Analysis
Lens_type <- info_merge %>%
filter(Product.type == "Lenses") %>%
group_by(Lens.type) %>%
summarise(number = n())
Lens Type with Age
Lens_data <- subset(info_merge, Age > 0 & Age < 100 & Lens.type != "Bifocal")
Lens_data$Lens.type <- factor(Lens_data$Lens.type, levels = c("Single vision", "Progressive"))
# ggplotly(
ggplot(Lens_data, aes(x = Lens.type, y = Age)) +
geom_boxplot() +
theme(axis.text.y=element_blank()) +
geom_jitter(alpha = 0.2) +
geom_violin(alpha = 0.25)#,

# width = 900
# )
Lens Coatings/Finishes by Age

Age Patterns
Age distribution
customers_age <- info_merge %>% filter(Age < 100, Age > 0, Amount > 0)
# Histogram of frequency of customers of certain age
# ggplotly(
ggplot(customers_age %>% distinct(Name, Age), aes(Age)) +
theme(axis.text.y=element_blank(), axis.text.x=element_blank()) +
geom_histogram(binwidth = 5, alpha = 0.8, col = "white") +
scale_x_continuous(breaks = seq(0, 100, 5))#,

# width = 900
# )
Total Spend against Age (For Each Customer)
Total_spend <- customers_age %>%
group_by(Name) %>%
summarise(Total.Spend = sum(Amount), Age = mean(Age))
# ggplotly(
ggplot(Total_spend, aes(x = Age, y = Total.Spend)) +
theme(axis.text.y=element_blank()) +
geom_point(shape = 1) +
geom_smooth() +
scale_x_continuous(breaks = seq(0, 100, 5)) +
scale_y_continuous(breaks = seq(0, 10000, 1000))#,

# width = 900
# )
Average Total Spend by Age
Regression Plot
Average_spend <- customers_age %>%
group_by(Name) %>%
summarise(Total.Spend = sum(Amount), Age = mean(Age)) %>%
group_by(Age) %>%
summarise(Average.spend = mean(Total.Spend))
# ggplotly(
ggplot(Average_spend, aes(x = Age, y = Average.spend)) +
theme(axis.text.y=element_blank()) +
geom_point(shape = 1) +
geom_smooth(method = "lm", formula = y ~ poly(x, 3)) +
scale_x_continuous(breaks = seq(0, 100, 10)) +
scale_y_continuous(breaks = seq(0, 10000, 100)) +
ggtitle("Regression of Average Spend with Age (3rd Order Polynomial)")#,

# width = 900
# )
Diagnostic Plots
Redacted
Frame cost and age
Product_age <- info_merge %>%
filter(Product.type %in% c("Frames", "Sunglasses"), Amount > 0, Age > 0, Age < 100, Amount < 2500)
# ggplotly(
ggplot(Product_age, aes(x = Age, y = Amount)) +
theme(axis.text.y=element_blank()) +
geom_point(shape = 1) +
geom_smooth()#,

# width = 900
# )
Customer Age Group - Customer Count and Spend

Purchase Frequency with Age

Visit Statistics
All charts are redacted ## Total number of purchases per customer
Total number of visits per customer
Number of purchases per visit
Number of Days Between Visits

Cumulative Graph of Number of Days to Customer Next Visit
If a customer visits again, 60% of the time a customer they will return within the first 90 days since their last visit.
ggplotly(
ggplot(visits %>% arrange(days_next_visit), aes(days_next_visit)) +
stat_ecdf(geom = "step", col = "blue", size = 0.5) +
scale_x_continuous(breaks = seq(0, 600, 30)) +
scale_y_continuous(breaks = seq(0, 1, 0.1)) +
ylab("Percentage"),
width = 900
)
Removed 903 rows containing non-finite values (stat_ecdf).
Customer ranking and percentile calculations
Note that table data has been redacted
Sales with Time
individual sales with time
# ggplotly(
ggplot(info_merge %>% filter(Amount > 0), aes(x = Order.date, y = Amount)) +
theme(axis.text.y=element_blank()) +
geom_point(shape = 1) +
geom_smooth()#,

# width = 900
# )
total daily sales with time
Sales_by_date <- info_merge %>%
group_by(Date = Order.date) %>%
summarise(Sales = sum(Amount, na.rm = TRUE))
# ggplotly(
ggplot(Sales_by_date, aes(x = Date, y = Sales)) +
theme(axis.text.y=element_blank()) +
geom_point(shape = 1) +
geom_smooth()#,

# width = 900
# )
total monthly sales with time
Monthly sales appear to rise and then tapper off, but the time frame is short so it might be a blip.
Sales_by_month <- info_merge %>%
mutate(Month = floor_date(Order.date, "month")) %>%
group_by(Month) %>%
summarise(month_sales = sum(Amount, na.rm = T))
# September sales is incomplete so excluded in this case
# ggplotly(
ggplot(Sales_by_month, aes(x = Month, y = month_sales, ymin = 0)) +
theme(axis.text.y=element_blank()) +
geom_point(size = 2) +
geom_smooth()

# width = 900
# )
unique customers plot earliest purchase date
Drop in sales might be due to drop in new customer acquisition rate.

Customer location mapping
post_codes <- read.csv("./Data/Postcodes.csv")
post_codes <- post_codes[ ,1]
coordinates <- read.csv("Postcodes coordinates.csv")
## install for newest verion of ggmap
#if(!requireNamespace("devtools")) install.packages("devtools")
#devtools::install_github("dkahle/ggmap", ref = "tidyup")
register_google(key = "AIzaSyD88nanfnGGg2iH5GqWsnznlNgad3SiBd4")
UK
90% of customers are located in the UK
ggmap(get_map("Machester", zoom = 6, api_key = key), extent = "device") +
geom_point(aes(x = lon, y = lat), data = coordinates,
alpha = .15, color="darkred", size = 2)
Source : https://maps.googleapis.com/maps/api/staticmap?center=Machester&zoom=6&size=640x640&scale=2&maptype=terrain&language=en-EN&key=xxx
Source : https://maps.googleapis.com/maps/api/geocode/json?address=Machester&key=xxx

Greater London
60% of all customers are located in London
ggmap(get_map("SW1W", zoom = 11, api_key = key), extent = "device") +
geom_point(aes(x = lon, y = lat), data = coordinates,
alpha = .15, color="darkred", size = 2)
Source : https://maps.googleapis.com/maps/api/staticmap?center=SW1W&zoom=11&size=640x640&scale=2&maptype=terrain&language=en-EN&key=xxx
Source : https://maps.googleapis.com/maps/api/geocode/json?address=SW1W&key=xxx

London
ggmap(get_map("SW1W", zoom = 12, api_key = key), extent = "device")+
geom_point(aes(x = lon, y = lat), data = coordinates,
alpha = .2, color="darkred", size = 2)
Source : https://maps.googleapis.com/maps/api/staticmap?center=SW1W&zoom=12&size=640x640&scale=2&maptype=terrain&language=en-EN&key=xxx
Source : https://maps.googleapis.com/maps/api/geocode/json?address=SW1W&key=xxx

London 2
ggmap(get_map("SW1W", zoom = 13, api_key = key), extent = "device")+
geom_point(aes(x = lon, y = lat), data = coordinates,
alpha = .25, color="darkred", size = 2)
Source : https://maps.googleapis.com/maps/api/staticmap?center=SW1W&zoom=13&size=640x640&scale=2&maptype=terrain&language=en-EN&key=xxx
Source : https://maps.googleapis.com/maps/api/geocode/json?address=SW1W&key=xxx

Sloane Square
ggmap(get_map("Sloane Square", zoom = 14, api_key = key), extent = "device")+
geom_point(aes(x = lon, y = lat), data = coordinates,
alpha = .25, color="darkred", size = 2)
Source : https://maps.googleapis.com/maps/api/staticmap?center=Sloane%20Square&zoom=14&size=640x640&scale=2&maptype=terrain&language=en-EN&key=xxx
Source : https://maps.googleapis.com/maps/api/geocode/json?address=Sloane%20Square&key=xxx

Walking Distance from Store
distance <- data.frame()
# $V1 takes it as a vector instead of data frame
London_regions <- read.csv("./Data/London Town and Boroughs.csv", stringsAsFactors = FALSE, header = FALSE)$V1
Post_code_regex <- "^([A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]? {1,2}[0-9][ABD-HJLN-UW-Z]{2}|GIR 0AA)$"
London_cust <- info %>%
filter(grepl(Post_code_regex, Post), tolower(Town) %in% tolower(London_regions), Amount > 0) %>%
distinct(Post)
Histogram of Walking Distance
# ggplotly(
ggplot(walking_dist, aes(minutes)) +
theme(axis.text.y=element_blank()) +
geom_histogram(position = 'stack', binwidth = 5, alpha = 0.9, col = "white", boundary = 0)#,

# width = 900
# )
Cumulative Graph of Walking Distance from Store
Only 30% of customers live within a 30 minute walk from the store. Some may work nearby, or the store may be a destination store instead of relying on local clientel.
ggplotly(
ggplot(walking_dist %>% arrange(minutes), aes(minutes)) +
stat_ecdf(geom = "step", col = "blue", size = 0.5) +
scale_x_continuous(breaks = seq(0, 300, 15)) +
scale_y_continuous(breaks = seq(0, 1, 0.1)) +
ylab("Percentage") +
ggtitle("Walking Distance from Store"),
width = 900
)
Age Demographics Comparison
# Categorises customer numbers and spending into age categories and filters by proximity of 45 min walk
age_summary <- function(lower, upper){
info_walk %>%
group_by(Name) %>%
summarise(spend = sum(Amount, na.rm = TRUE), Age = Age[1], minutes = minutes[1]) %>% # as each name has multiple only take 1st age&min
filter(Age >= lower, Age <= upper, spend > 0, minutes > 30) %>%
summarise(count = n_distinct(Name))
}
# Census data
census <- read.csv("./Census data/Census area summary.csv", stringsAsFactors = FALSE)
TD_count <- mapply(age_summary, lower = census$Lower, upper = census$Upper)
TD_count <- matrix(unlist(t(TD_count)), ncol = 1) #Transposes then converts the list to a matrix
colnames(TD_count) <- "TD_count"
census_sum <- cbind(census, TD_count)
census_sum <- mutate(census_sum, count_percent = TD_count/sum(TD_count))
age_group <- paste(census_sum[ ,1], "-", census_sum[ ,2], sep = "")
comparison <- cbind(age_group, census_sum[,3:6])
colnames(comparison) <- c("age_group", "Count", "Census", "TD_count", "TD customers")
comp_long <- cbind(age_group, gather(comparison[,2:5]))
comp_long <- subset(comp_long, key %in% c("Census", "TD customers"))
# keep data as factors, otherwise it sorts by numerical order
comp_long$age_group <- factor(comp_long$age_group, levels = unique(comp_long$age_group))
comp_long$key <- factor(comp_long$key, levels = unique(comp_long$key))
For age comparison it only looks at 160 out of 940 customer, after filtering for 45min walking proximity, age data and spending. Overall compared to the local area the store attracts a much larger proportion of those aged between 45-59 compared to other ages groups.
comp_long <- comp_long %>% mutate(percent = value*100)
# ggplotly(
ggplot(comp_long, aes(x = age_group, y = percent, fill = key)) +
theme(axis.text.x=element_blank()) +
geom_bar(stat = "identity", position = position_dodge()) +
xlab("Age Categories") +
ylab("Percentage") +
ggtitle("Comparison of Age Groups (2011 K&C Census vs Store)") +
scale_y_continuous(breaks = seq(0, 100, 10))#,

# width = 900,
# height = 500
# )
LS0tDQp0aXRsZTogIkRhdGEgQW5hbHlzaXMgb24gUmV0YWlsIERhdGEiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICBodG1sX2RvY3VtZW50Og0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIHRvYzogeWVzDQotLS0NCiAgIA0KRGF0ZTogMjEvMDQvMTYgICANCkF1dGhvcjogSm9uYXRoYW4gQ2hhbiAgIA0KRGVzY3JpcHRpb246IERhdGEgQW5hbHlzaXMgb24gUmV0YWlsIERhdGEgICANCg0KIyBJbml0aWFsIERhdGEgTG9hZGluZw0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSgidGlkeXZlcnNlIikNCmxpYnJhcnkoImx1YnJpZGF0ZSIpDQpsaWJyYXJ5KCJnZ21hcCIpDQpsaWJyYXJ5KCJwbG90bHkiKQ0KbGlicmFyeSgidmlyaWRpcyIpDQpsaWJyYXJ5KCJ3ZXNhbmRlcnNvbiIpDQpsaWJyYXJ5KCJEVCIpDQpgYGANCg0KSW5pdGlhbGlzaW5nIGRhdGEsIHdoaWNoIGluY2x1ZGVzIG9yaWdpbmFsIGRhdGEgYW5kIG5ldyBkYXRhLCBqb2luaW5nIHRoZW0gdG8gY3JlYXRlIGEgbWFzdGVyIHNldC4NCmBgYHtyfQ0KaW5mbyA8LSByZWFkLnRhYmxlKCIuL0RhdGEvQWxsX2luZm8udHh0IiwgaGVhZGVyID0gVFJVRSwgbmEuc3RyaW5ncyA9IGMoIiIsICJOQSIpLCANCiAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQppbmZvX25ldyA8LSByZWFkLmNzdigiLi9EYXRhL0FsbF9pbmZvKG5ldyktMS5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIGhlYWRlciA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICBuYS5zdHJpbmdzID0gYygiIiwgIk5BIikpDQpjb2xuYW1lcyhpbmZvX25ldykgPC0gY29sbmFtZXMoaW5mbykNCmluZm9fdGVtcCA8LSByYmluZChpbmZvLCBpbmZvX25ldykNCg0KaW5mb193YWxrIDwtIHJlYWQuY3N2KCIuL0RhdGEvaW5mbyB3YWxrLTEuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KYGBgDQpPdmVyYWxsIHRoZXJlIGFyZSBgciBucm93KGluZm9fbWVyZ2UpYCByb3dzIG9mIGRhdGEuIFdpdGggYHIgbl9kaXN0aW5jdChpbmZvX21lcmdlJE5hbWUpYCB1bmlxdWUgY3VzdG9tZXJzLiAgIA0KDQpDb252ZXJ0cyB0aGUgZGF0ZXMgaW4gY2hhcmFjdGVyIGZvcm1hdCB0byBkYXRlIGZvcm1hdCBhbmQgY2FsY3VsYXRlIGFnZSBmcm9tIGRhdGUgb2YgYmlydGguDQpSZWRhY3RlZCBhIGxvdCBvZiBjb2x1bW5zDQpgYGB7cn0NCmluZm9fbWVyZ2UgPC0gaW5mb190ZW1wDQppbmZvX21lcmdlJE9yZGVyLmRhdGUgPC0gZG15KGluZm9fdGVtcCRPcmRlci5kYXRlKQ0KaW5mb19tZXJnZSRELk8uQi4gPC0gZG15KGluZm9fdGVtcCRELk8uQi4pDQppbmZvX21lcmdlIDwtIGluZm9fbWVyZ2UgJT4lIG11dGF0ZShBZ2UgPSB5ZWFyKGFzLnBlcmlvZChpbnRlcnZhbChpbmZvX21lcmdlJEQuTy5CLiwgIjIxLzA0LzE2IikpKSkNCnNlbGVjdChpbmZvX21lcmdlWzQ1OjU0LCBdLCAtYyhOYW1lLCBQb3N0LCBELk8uQi4sIEFnZSwgU2V4LCBHcm9zcy5wcm9maXQsIEFtb3VudC4xLCBWYWx1ZSwgT3JkZXIudmFsdWUsIE5ldC5PcmRlci5WYWx1ZSwgUHJvai4uVmFsdWUudy5vLmRpc2NvdW50LCBQcm9qLi5Db3N0Lm9mLnNhbGVzLCBBbW91bnQpKQ0KYGBgDQoNCiMgQ2F0ZWdvcnkgQW5hbHlzaXMNClRvIHN0YXJ0IG9mIHdpdGggc29tZSBkYXRhIGNsZWFuc2luZyB0byBtZXJnZSBkaWZmZXJlbnQgdHlwZSBvZiBob3JuIHByb2R1Y3RzIGludG8gYSBzaW5nbGUgZ3JvdXAuDQpgYGB7cn0NCmluZm9fbWVyZ2UkTWF0ZXJpYWwgPC0gZ3N1YihwYXR0ZXJuID0gIkJ1ZmZhbG8gSG9ybiIsIHJlcGxhY2UgPSAiSG9ybiIsIHggPSBpbmZvX21lcmdlJE1hdGVyaWFsKQ0KaW5mb19tZXJnZSRNYXRlcmlhbCA8LSBnc3ViKHBhdHRlcm4gPSAiQ2FyYm9uIEhvcm4iLCByZXBsYWNlID0gIkhvcm4iLCB4ID0gaW5mb19tZXJnZSRNYXRlcmlhbCkNCmluZm9fbWVyZ2UkTWF0ZXJpYWwgPC0gZ3N1YihwYXR0ZXJuID0gIlRoaW4gSG9ybiIsIHJlcGxhY2UgPSAiSG9ybiIsIHggPSBpbmZvX21lcmdlJE1hdGVyaWFsKQ0KYGBgDQojIyBCeSBNYXRlcmlhbA0KIyMjIE1hdGVyaWFsIHdpdGggQWdlDQpgYGB7cn0NCm1hdGVyaWFsX3Bsb3QgPC0gZ2dwbG90KGZpbHRlcihpbmZvX21lcmdlLCBBZ2UgPiAwICYgQWdlIDwgMTAwLCANCiAgICAgICAgICAgICAgTWF0ZXJpYWwgJWluJSBjKCJBY2V0YXRlIiwgIkhvcm4iLCAiQ29tYmluYXRpb24iLCAiVGl0YW5pdW0iLCAiUmltbGVzcyIpKSwgDQogICAgICAgICAgICAgYWVzKHggPSBNYXRlcmlhbCwgeSA9IEFnZSwgZmlsbCA9IE1hdGVyaWFsKSkgKw0KICB0aGVtZShheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMikgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9VCkgKw0KICBnZW9tX3Zpb2xpbihhbHBoYSA9IDAuMjUpDQogIGdndGl0bGUoIkZyYW1lIE1hdGVyaWFsIHdpdGggQWdlIikNCg0KIyBnZ3Bsb3RseShtYXRlcmlhbF9wbG90LCB3aWR0aCA9IDkwMCkNCiAgbWF0ZXJpYWxfcGxvdA0KYGBgDQpgYGB7cn0NCm1hdC50eXBlIDwtIGRhdGEuZnJhbWUoKQ0KbWF0LnR5cGUgPC0gaW5mb19tZXJnZSAlPiUNCiAgZmlsdGVyKFByb2R1Y3QudHlwZSAlaW4lIGMoIkZyYW1lcyIsICJTdW5nbGFzc2VzIiksDQogICAgICAgICBNYXRlcmlhbCAlaW4lIGMoIkFjZXRhdGUiLCAiSG9ybiIsICJDb21iaW5hdGlvbiIsICJUaXRhbml1bSIsICJSaW1sZXNzIiksDQogICAgICAgICBBbW91bnQgPiAwLCAgIyB0YWtlcyBvbmx5IGZyYW1lcyBtYWRlIGJ5IFREDQogICAgICAgICBpcy5uYShTZXgpID09IEZBTFNFKSAlPiUNCiAgZ3JvdXBfYnkoUHJvZHVjdC50eXBlLCBNYXRlcmlhbCwgU2V4KSAlPiUNCiAgc3VtbWFyaXNlKG9yZGVycyA9IG4oKSwgVG90YWwuT3JkZXIudmFsdWUgPSBzdW0oQW1vdW50LCBuYS5ybSA9IFRSVUUpLCBuby5jdXN0b21lcnMgPSBuX2Rpc3RpbmN0KE5hbWUpKQ0KDQptYXQudHlwZSA8LSBtdXRhdGUobWF0LnR5cGUsIA0KICAgICAgICAgICAgICBQZXJjZW50YWdlLk9yZGVycyA9IG9yZGVycy9zdW0obWF0LnR5cGUkb3JkZXJzKSwgICMgQ3JlYXRlcyBwcm9maXQvb3JkZXIgYW5kICUgb2Ygb3JkZXJzDQogICAgICAgICAgICAgIFBlcmNlbnRhZ2UuT3JkZXIuVmFsdWUgPSBUb3RhbC5PcmRlci52YWx1ZS9zdW0obWF0LnR5cGUkVG90YWwuT3JkZXIudmFsdWUpLA0KICAgICAgICAgICAgICBBdmVyYWdlLk9yZGVyLnZhbHVlID0gVG90YWwuT3JkZXIudmFsdWUvb3JkZXJzLA0KICAgICAgICAgICAgICBPcmRlci5jdXN0b21lci5yYXRpbyA9IG9yZGVycy9uby5jdXN0b21lcnMpDQoNCmBgYA0KIyMgTGVucyBBbmFseXNpcw0KYGBge3J9DQpMZW5zX3R5cGUgPC0gaW5mb19tZXJnZSAlPiUgDQogIGZpbHRlcihQcm9kdWN0LnR5cGUgPT0gIkxlbnNlcyIpICU+JQ0KICBncm91cF9ieShMZW5zLnR5cGUpICU+JQ0KICBzdW1tYXJpc2UobnVtYmVyID0gbigpKQ0KYGBgDQoNCiMjIyBMZW5zIFR5cGUgd2l0aCBBZ2UNCmBgYHtyfQ0KTGVuc19kYXRhIDwtIHN1YnNldChpbmZvX21lcmdlLCBBZ2UgPiAwICYgQWdlIDwgMTAwICYgTGVucy50eXBlICE9ICJCaWZvY2FsIikNCkxlbnNfZGF0YSRMZW5zLnR5cGUgPC0gZmFjdG9yKExlbnNfZGF0YSRMZW5zLnR5cGUsIGxldmVscyA9IGMoIlNpbmdsZSB2aXNpb24iLCAiUHJvZ3Jlc3NpdmUiKSkNCiMgZ2dwbG90bHkoDQogIGdncGxvdChMZW5zX2RhdGEsIGFlcyh4ID0gTGVucy50eXBlLCB5ID0gQWdlKSkgKw0KICAgIGdlb21fYm94cGxvdCgpICsNCiAgICB0aGVtZShheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCkpICsNCiAgICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMikgKw0KICAgIGdlb21fdmlvbGluKGFscGhhID0gMC4yNSkjLA0KIyAgIHdpZHRoID0gOTAwDQojICkNCmBgYA0KDQojIyMgTGVucyBDb2F0aW5ncy9GaW5pc2hlcyBieSBBZ2UNCmBgYHtyfQ0KRmluaXNoX3R5cGUgPC0gaW5mb19tZXJnZSAlPiUgDQogIGZpbHRlcihQcm9kdWN0LnR5cGUgPT0gIkxlbnNlcyIpICU+JQ0KICBncm91cF9ieShGaW5pc2gpICU+JQ0KICBzdW1tYXJpc2UobnVtYmVyID0gbigpKQ0Kd3JpdGUuY3N2KEZpbmlzaF90eXBlLCAiZmluaXNoLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQ0KDQpMZW5zX3R5cGVzIDwtIHJlYWQuY3N2KCJMZW5zIHR5cGUuY3N2IikNCmluZm9fbGVucyA8LSBtZXJnZSh4ID0gaW5mb19tZXJnZSwgeSA9IExlbnNfdHlwZXMsIGJ5LnggPSAiRmluaXNoIiwgYnkueSA9ICJGaW5pc2giKQ0KaW5mb19sZW5zIDwtIGZpbHRlcihpbmZvX2xlbnMsIEFnZSA8IDEwMCwgQWdlID4gMCkNCmluZm9fbGVucyRUeXBlIDwtIGZhY3RvcihpbmZvX2xlbnMkVHlwZSwgbGV2ZWxzID0gYygiU2luZ2xlIFZpc2lvbiIsICJEaWdpdGFsIiwgIk9mZmljZSBMZW5zIiwgIlByb2dyZXNzaXZlIikpDQoNCiMgZ2dwbG90bHkoDQogIGdncGxvdChpbmZvX2xlbnMsIGFlcyh4ID0gVHlwZSwgeSA9IEFnZSwgZmlsbCA9IFR5cGUpKSArDQogICAgZ2VvbV9ib3hwbG90KCkgKw0KICAgIHRoZW1lKGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSkgKw0KICAgIGdlb21faml0dGVyKGFscGhhID0gMC4yKSArDQogICAgZ2VvbV92aW9saW4oYWxwaGEgPSAwLjI1KSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gd2VzX3BhbGV0dGUoIk1vb25yaXNlMyIsIDQsIHR5cGUgPSAiZGlzY3JldGUiKSkjLA0KIyAgIHdpZHRoID0gOTAwDQojICkNCmBgYA0KDQojIEFnZSBQYXR0ZXJucw0KIyMgQWdlIGRpc3RyaWJ1dGlvbg0KYGBge3J9DQpjdXN0b21lcnNfYWdlIDwtIGluZm9fbWVyZ2UgJT4lIGZpbHRlcihBZ2UgPCAxMDAsIEFnZSA+IDAsIEFtb3VudCA+IDApDQoNCiMgIEhpc3RvZ3JhbSBvZiBmcmVxdWVuY3kgb2YgY3VzdG9tZXJzIG9mIGNlcnRhaW4gYWdlDQojIGdncGxvdGx5KA0KICBnZ3Bsb3QoY3VzdG9tZXJzX2FnZSAlPiUgZGlzdGluY3QoTmFtZSwgQWdlKSwgYWVzKEFnZSkpICsNCiAgICAgICAgdGhlbWUoYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpICsNCiAgICAgICAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1LCBhbHBoYSA9IDAuOCwgY29sID0gIndoaXRlIikgKw0KICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgNSkpIywNCiMgICB3aWR0aCA9IDkwMA0KIyApDQpgYGANCg0KIyMgVG90YWwgU3BlbmQgYWdhaW5zdCBBZ2UgKEZvciBFYWNoIEN1c3RvbWVyKQ0KYGBge3J9DQpUb3RhbF9zcGVuZCA8LSBjdXN0b21lcnNfYWdlICU+JSANCiAgICAgICAgICAgICAgICBncm91cF9ieShOYW1lKSAlPiUgDQogICAgICAgICAgICAgICAgc3VtbWFyaXNlKFRvdGFsLlNwZW5kID0gc3VtKEFtb3VudCksIEFnZSA9IG1lYW4oQWdlKSkNCiMgZ2dwbG90bHkoDQogIGdncGxvdChUb3RhbF9zcGVuZCwgYWVzKHggPSBBZ2UsIHkgPSBUb3RhbC5TcGVuZCkpICsgDQogICAgdGhlbWUoYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpKSArDQogICAgZ2VvbV9wb2ludChzaGFwZSA9IDEpICsNCiAgICBnZW9tX3Ntb290aCgpICsNCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgNSkpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMDAwLCAxMDAwKSkjLA0KIyAgIHdpZHRoID0gOTAwDQojICkNCmBgYA0KDQojIyBBdmVyYWdlIFRvdGFsIFNwZW5kIGJ5IEFnZSB7LnRhYnNldH0NCiMjIyBSZWdyZXNzaW9uIFBsb3QNCmBgYHtyfQ0KQXZlcmFnZV9zcGVuZCA8LSBjdXN0b21lcnNfYWdlICU+JSANCiAgZ3JvdXBfYnkoTmFtZSkgJT4lIA0KICBzdW1tYXJpc2UoVG90YWwuU3BlbmQgPSBzdW0oQW1vdW50KSwgQWdlID0gbWVhbihBZ2UpKSAlPiUNCiAgZ3JvdXBfYnkoQWdlKSAlPiUgDQogIHN1bW1hcmlzZShBdmVyYWdlLnNwZW5kID0gbWVhbihUb3RhbC5TcGVuZCkpDQoNCiMgZ2dwbG90bHkoDQogIGdncGxvdChBdmVyYWdlX3NwZW5kLCBhZXMoeCA9IEFnZSwgeSA9IEF2ZXJhZ2Uuc3BlbmQpKSArIA0KICAgIHRoZW1lKGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSkgKw0KICAgIGdlb21fcG9pbnQoc2hhcGUgPSAxKSArDQogICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHkgfiBwb2x5KHgsIDMpKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIDEwKSkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwMDAsIDEwMCkpICsgDQogICAgZ2d0aXRsZSgiUmVncmVzc2lvbiBvZiBBdmVyYWdlIFNwZW5kIHdpdGggQWdlICgzcmQgT3JkZXIgUG9seW5vbWlhbCkiKSMsDQojICAgd2lkdGggPSA5MDANCiMgKQ0KYGBgDQojIyMgTW9kZWwgU3VtbWFyeQ0KUmVkYWN0ZWQNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQphZ2Vfc3BlbmRpbmcgPC0gbG0oQXZlcmFnZV9zcGVuZCRBdmVyYWdlLnNwZW5kIH4gcG9seShBdmVyYWdlX3NwZW5kJEFnZSwgMykpDQoNCnN1bW1hcnkoYWdlX3NwZW5kaW5nKQ0KYGBgDQojIyMgRGlhZ25vc3RpYyBQbG90cw0KUmVkYWN0ZWQNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpwYXIobWZyb3cgPSBjKDIsIDIpKQ0KcGxvdChhZ2Vfc3BlbmRpbmcpDQpgYGANCg0KDQojIyBGcmFtZSBjb3N0IGFuZCBhZ2UNCmBgYHtyfQ0KUHJvZHVjdF9hZ2UgPC0gaW5mb19tZXJnZSAlPiUNCiAgZmlsdGVyKFByb2R1Y3QudHlwZSAlaW4lIGMoIkZyYW1lcyIsICJTdW5nbGFzc2VzIiksIEFtb3VudCA+IDAsIEFnZSA+IDAsIEFnZSA8IDEwMCwgQW1vdW50IDwgMjUwMCkNCiMgZ2dwbG90bHkoDQogIGdncGxvdChQcm9kdWN0X2FnZSwgYWVzKHggPSBBZ2UsIHkgPSBBbW91bnQpKSArIA0KICAgIHRoZW1lKGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSkgKw0KICAgIGdlb21fcG9pbnQoc2hhcGUgPSAxKSArDQogICAgZ2VvbV9zbW9vdGgoKSMsDQojICAgd2lkdGggPSA5MDANCiMgKQ0KYGBgDQoNCiMjIEN1c3RvbWVyIEFnZSBHcm91cCAtIEN1c3RvbWVyIENvdW50IGFuZCBTcGVuZA0KYGBge3J9DQpBZ2VfcmFuZ2UgPC0gY2JpbmQoc2VxKDEsIDgxLCA1KSwgc2VxKDUsIDg1LCA1KSkNCkFnZV9sYWJlbCA8LSBwYXN0ZShBZ2VfcmFuZ2VbLDFdLCAiLSIsIEFnZV9yYW5nZVssMl0sIHNlcCA9ICIiKQ0KaW5mb19tZXJnZSRBZ2VfZ3JvdXAgPC0gTkENCg0KaW5mb19tZXJnZSRBZ2VfZ3JvdXAgPC0gY3V0KGluZm9fbWVyZ2UkQWdlLCBicmVha3MgPSBzZXEoMCwgODUsIDUpLCBsYWJlbHMgPSBBZ2VfbGFiZWwpDQoNCmFnZV9ncm91cGluZyA8LSBpbmZvX21lcmdlICU+JQ0KICBmaWx0ZXIoQW1vdW50ID4gMCwgQWdlX2dyb3VwICE9ICJOQSIpICU+JQ0KICBncm91cF9ieShBZ2VfZ3JvdXApICU+JQ0KICBzdW1tYXJpc2UoQ291bnQgPSBuX2Rpc3RpbmN0KE5hbWUpLCBUb3RhbF9hbW91bnQgPSBzdW0oQW1vdW50LCBuYS5ybSA9IFRSVUUpKSAlPiUNCiAgbXV0YXRlKHBlcl9jb3VudCA9IENvdW50L3N1bShDb3VudCkqMTAwLCBwZXJfYW1vdW50ID0gVG90YWxfYW1vdW50L3N1bShUb3RhbF9hbW91bnQpKjEwMCkNCg0KaW5mb19tZXJnZSAlPiUNCiAgZmlsdGVyKEFtb3VudCA+IDApICU+JQ0KICBjb3VudChBZ2VfZ3JvdXApDQoNCnJhbmtfcDIgPC0gaW5mb19tZXJnZSAlPiUgDQogIGZpbHRlcihBbW91bnQgPiAwKSAlPiUNCiAgZ3JvdXBfYnkoTmFtZSkgJT4lDQogIHN1bW1hcmlzZShUb3RhbC5TcGVuZCA9IHN1bShBbW91bnQsIG5hLnJtID0gVFJVRSksIEFnZSA9IG1lYW4oQWdlKSkNCiAgcmFua19wMiAlPiUgbXV0YXRlKGFnZV9ncm91cCA9IGN1dChyYW5rX3AyJEFnZSwgYnJlYWtzID0gc2VxKDAsIDg1LCA1KSwgbGFiZWxzID0gQWdlX2xhYmVsKSkgJT4lDQogIGNvdW50KGFnZV9ncm91cCkNCg0KICAgIA0KYWdlX2dyb3VwaW5nMiA8LSBhZ2VfZ3JvdXBpbmdbLCBjKCJBZ2VfZ3JvdXAiLCAicGVyX2NvdW50IiwgInBlcl9hbW91bnQiKV0NCmNvbG5hbWVzKGFnZV9ncm91cGluZzIpIDwtIGMoIkFnZS5Hcm91cCIsICJOdW1iZXIiLCAiU3BlbmQiKQ0KYWdlX2dyb3VwaW5nMiA8LSBnYXRoZXIoYWdlX2dyb3VwaW5nMiwgIlR5cGUiLCAiUGVyY2VudCIsIE51bWJlciwgU3BlbmQpDQoNCmFnZV9wbG90IDwtIGdncGxvdChhZ2VfZ3JvdXBpbmcyLCBhZXMoeCA9IEFnZS5Hcm91cCwgeSA9IFBlcmNlbnQsIGdyb3VwID0gVHlwZSwgY29sID0gVHlwZSwgZmlsbCA9IFR5cGUpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBhbHBoYSA9IDAuOCwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgpKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDI1LCA1KSkgKw0KICBnZ3RpdGxlKCJBZ2UgR3JvdXAgQnJlYWtkb3duIikgKw0KICB5bGFiKCJQZXJjZW50IikgKw0KICB4bGFiKCJBZ2UgR3JvdXAiKQ0KDQojIGdncGxvdGx5KGFnZV9wbG90LCB3aWR0aCA9IDkwMCkNCmFnZV9wbG90DQpgYGANCiMjIFB1cmNoYXNlIEZyZXF1ZW5jeSB3aXRoIEFnZQ0KYGBge3J9DQojIExvb2tzIGF0IGZyZXF1ZW5jeSBvZiB0cmFuc2FjdGlvbnMgYmFzZWQgb24gYWdlDQpjb3VudF9hZ2UgPC0gaW5mb19tZXJnZSAlPiUgDQogIGdyb3VwX2J5KE5hbWUpICU+JSANCiAgc3VtbWFyaXNlKE5vLm9yZGVycyA9IG5fZGlzdGluY3QoT3JkZXIubnVtYmVyKSwgQWdlID0gbWVhbihBZ2UpKSAlPiUNCiAgZ3JvdXBfYnkoQWdlKSAlPiUgDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lIA0KICBmaWx0ZXIoY291bnQgPiAyLCBBZ2UgPCAxMDAsIEFnZSA+IDApDQojIGF2ZXJhZ2UgbnVtYmVyIG9mIG9yZGVycyBiYXNlZCBvbiBhZ2UNCmZyZXFfYWdlIDwtIGluZm9fbWVyZ2UgJT4lIA0KICBncm91cF9ieShOYW1lKSAlPiUgDQogIHN1bW1hcmlzZShOby5vcmRlcnMgPSBuX2Rpc3RpbmN0KE9yZGVyLm51bWJlciksIEFnZSA9IG1lYW4oQWdlKSkgJT4lDQogIGdyb3VwX2J5KEFnZSkgJT4lDQogIGZpbHRlcihBZ2UgJWluJSBjb3VudF9hZ2UkQWdlLCBBZ2UgIT0gMSkgJT4lDQogIHN1bW1hcmlzZShhdmVyYWdlID0gbWVhbihOby5vcmRlcnMpKQ0KIyBwbG90cyB0aGUgYWJvdmUgYXZlcmFnZSBudW1lYmVyIG9mIHB1cmNoYXNlcyBic2VkIG9uIGFnZQ0KIyBnZ3Bsb3RseSgNCiAgZ2dwbG90KGZyZXFfYWdlLCBhZXMoQWdlLCBhdmVyYWdlKSkgKw0KICAgIHRoZW1lKGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSkgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgZ2VvbV9zbW9vdGgoKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIDEwKSkgKw0KICAgIHlsYWIoIk51bWJlciBvZiBQdXJjaGFzZXMiKSMsDQojICAgd2lkdGggPSA5MDANCiMgKQ0KYGBgDQojIFZpc2l0IFN0YXRpc3RpY3MNCkFsbCBjaGFydHMgYXJlIHJlZGFjdGVkDQojIyBUb3RhbCBudW1iZXIgb2YgcHVyY2hhc2VzIHBlciBjdXN0b21lcg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCiMgbG9va3MgYXQgdGhlIGZyZXF1ZW5jeSBvZiBwdXJjaGFzZXMgJiB2aXNpdHMNCmZyZXFfcHVyY2hhc2VfdmlzaXRzIDwtIGluZm9fbWVyZ2UgJT4lIA0KICBmaWx0ZXIoQW1vdW50ID4gMCwgUHJvZHVjdC50eXBlICVpbiUgYygiU3VuZ2xhc3NlcyIsICJGcmFtZXMiKSkgJT4lDQogIGdyb3VwX2J5KE5hbWUpICU+JQ0KICBzdW1tYXJpc2UocHVyY2hhc2VzID0gbigpLCB2aXNpdHMgPSBuX2Rpc3RpbmN0KE9yZGVyLmRhdGUpKSANCnB1cmNoYXNlX251bWJlciA8LSBmcmVxX3B1cmNoYXNlX3Zpc2l0cyAlPiUgY291bnQocHVyY2hhc2VzKQ0KDQpwbG90X2x5KHB1cmNoYXNlX251bWJlciwgbGFiZWxzID0gfnB1cmNoYXNlcywgdmFsdWVzID0gfm4sIHR5cGUgPSAicGllIiwgd2lkdGggPSA5MDApDQpgYGANCiMjIFRvdGFsIG51bWJlciBvZiB2aXNpdHMgcGVyIGN1c3RvbWVyDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KIyBzYW1lIGJ1dCBmb3IgdmlzdHMgZnJlcXVlbmN5DQpmcmVxX3Zpc2l0cyA8LSBmcmVxX3B1cmNoYXNlX3Zpc2l0cyAlPiUgY291bnQodmlzaXRzKQ0KDQpwbG90X2x5KGZyZXFfdmlzaXRzLCBsYWJlbHMgPSB+dmlzaXRzLCB2YWx1ZXMgPSB+biwgdHlwZSA9ICJwaWUiLCB3aWR0aCA9IDkwMCkNCmBgYA0KDQojIyBOdW1iZXIgb2YgcHVyY2hhc2VzIHBlciB2aXNpdA0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCiMgbnVtYmVyIG9mIHB1cmNoYXNlcyBvbiBzYW1lIGRheSBieSB2aXNpdCwgZnJhbWVzIG9ubHkNCm5vX3B1cmNoYXNlX2RheSA8LSBpbmZvX21lcmdlICU+JSANCiAgZmlsdGVyKEFtb3VudCA+IDAsIFByb2R1Y3QudHlwZSAlaW4lIGMoIlN1bmdsYXNzZXMiLCAiRnJhbWVzIikpICU+JQ0KICBncm91cF9ieShOYW1lLCBPcmRlci5kYXRlKSAlPiUgDQogIHN1bW1hcmlzZShwdXJjaGFzZXMgPSBuKCkpICU+JQ0KICBjb3VudChwdXJjaGFzZXMpICU+JQ0KICBtdXRhdGUocGVyY2VudGFnZSA9IG4vc3VtKG4pKjEwMCkNCg0KcGxvdF9seShub19wdXJjaGFzZV9kYXksIGxhYmVscyA9IH5wdXJjaGFzZXMsIHZhbHVlcyA9IH5uLCB0eXBlID0gInBpZSIsIHdpZHRoID0gOTAwKQ0KYGBgDQojIyBOdW1iZXIgb2YgRGF5cyBCZXR3ZWVuIFZpc2l0cw0KYGBge3J9DQptdWx0aV9idXlfdmlzaXQgPC0gaW5mb19tZXJnZSAlPiUgDQogIGZpbHRlcihBbW91bnQgPiAwLCBQcm9kdWN0LnR5cGUgJWluJSBjKCJTdW5nbGFzc2VzIiwgIkZyYW1lcyIpKSAlPiUNCiAgZ3JvdXBfYnkoTmFtZSwgT3JkZXIuZGF0ZSkgJT4lIA0KICBzdW1tYXJpc2UocHVyY2hhc2VzID0gbigpKSAlPiUgDQogIGNvdW50KHB1cmNoYXNlcykNCg0KIyBjYWxjdWxhdGVzIHRoZSBudW1iZXIgb2YgZGF5cyBiZXR3ZWVuIHRoZSBvcmRlciBkYXRlIGFuZCB0aGUgbGFzdCBvcmRlciBkYXRlDQp2aXNpdHMgPC0gaW5mb19tZXJnZSAlPiUgDQogIGZpbHRlcihQcm9kdWN0LnR5cGUgJWluJSBjKCJGcmFtZXMiLCAiU3VuZ2xhc3NlcyIpLCBBbW91bnQgPiAwKSAlPiUNCiAgZ3JvdXBfYnkoTmFtZSkgJT4lDQogIGRpc3RpbmN0KE9yZGVyLmRhdGUpICU+JQ0KICBhcnJhbmdlKE5hbWUsIE9yZGVyLmRhdGUpICU+JQ0KICBtdXRhdGUoZGF5c19uZXh0X3Zpc2l0ID0gT3JkZXIuZGF0ZSAtIGxhZyhPcmRlci5kYXRlKSkNCg0KIyBwbG90cyB0aGUgbnVtYmVyIG9mIGRheXMgdW50aWwgdGhlaXIgbmV4dCB2aXNpdA0KIyBnZ3Bsb3RseSgNCiAgZ2dwbG90KHZpc2l0cywgYWVzKGRheXNfbmV4dF92aXNpdCkpICsNCiAgICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDMwLCBhbHBoYSA9IDAuNzUsIGNvbCA9ICJ3aGl0ZSIsIGZpbGwgPSAiRGFyayBCbHVlIiwgYm91bmRhcnk9MCkrDQogICAgdGhlbWUoYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCA2MDAsIDMwKSkgKw0KICAgIGdndGl0bGUoIkRheXMgdG8gTmV4dCBWaXNpdCIpICsNCiAgICB4bGFiKCJOdW1iZXIgb2YgRGF5cyIpICsNCiAgICB5bGFiKCJOdW1iZXIgb2YgVmlzaXRzIikjLA0KIyAgIHdpZHRoID0gOTAwDQojICkNCmBgYA0KIyMgQ3VtdWxhdGl2ZSBHcmFwaCBvZiBOdW1iZXIgb2YgRGF5cyB0byBDdXN0b21lciBOZXh0IFZpc2l0DQpJZiBhIGN1c3RvbWVyIHZpc2l0cyBhZ2FpbiwgNjAlIG9mIHRoZSB0aW1lIGEgY3VzdG9tZXIgdGhleSB3aWxsIHJldHVybiB3aXRoaW4gdGhlIGZpcnN0IDkwIGRheXMgc2luY2UgdGhlaXIgbGFzdCB2aXNpdC4NCmBgYHtyfQ0KZ2dwbG90bHkoDQogIGdncGxvdCh2aXNpdHMgJT4lIGFycmFuZ2UoZGF5c19uZXh0X3Zpc2l0KSwgYWVzKGRheXNfbmV4dF92aXNpdCkpICsgDQogICAgc3RhdF9lY2RmKGdlb20gPSAic3RlcCIsIGNvbCA9ICJibHVlIiwgc2l6ZSA9IDAuNSkgKw0KICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgNjAwLCAzMCkpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEsIDAuMSkpICsNCiAgICB5bGFiKCJQZXJjZW50YWdlIiksDQogIHdpZHRoID0gOTAwDQopDQpgYGANCiMjIEN1c3RvbWVyIHJhbmtpbmcgYW5kIHBlcmNlbnRpbGUgY2FsY3VsYXRpb25zDQpOb3RlIHRoYXQgdGFibGUgZGF0YSBoYXMgYmVlbiByZWRhY3RlZA0KYGBge3J9DQoNCnBlcmNlbnRpbGVfbGFiZWwgPC0gcGFzdGUoc2VxKDAsIDk1LCA1KSwgIi0iLCBzZXEoNSwgMTAwLCA1KSwgc2VwID0iIikNCkFnZV9yYW5nZSA8LSBjYmluZChzZXEoMSwgODEsIDUpLCBzZXEoNSwgODUsIDUpKQ0KQWdlX2xhYmVsIDwtIHBhc3RlKEFnZV9yYW5nZVssMV0sICItIiwgQWdlX3JhbmdlWywyXSwgc2VwID0gIiIpDQoNCnJhbmtfcDEgPC0gaW5mb19tZXJnZSAlPiUgDQogIGZpbHRlcihBbW91bnQgPiAwLCBQcm9kdWN0LnR5cGUgJWluJSBjKCJTdW5nbGFzc2VzIiwgIkZyYW1lcyIpKSAlPiUNCiAgZ3JvdXBfYnkoTmFtZSkgJT4lDQogIHN1bW1hcmlzZShGcmFtZXMuQm91Z2h0ID0gbigpLCBWaXNpdHMgPSBuX2Rpc3RpbmN0KE9yZGVyLmRhdGUpKQ0KcmFua19wMiA8LSBpbmZvX21lcmdlICU+JSANCiAgZmlsdGVyKEFtb3VudCA+IDApICU+JQ0KICBncm91cF9ieShOYW1lKSAlPiUNCiAgc3VtbWFyaXNlKFRvdGFsLlNwZW5kID0gc3VtKEFtb3VudCwgbmEucm0gPSBUUlVFKSwgQWdlID0gbWVhbihBZ2UpKQ0KcmFua2luZyA8LSBtZXJnZShyYW5rX3AxLCByYW5rX3AyLCBhbGwueSA9IFRSVUUpDQoNCnJhbmtpbmcgPC0gcmFua2luZyAlPiUgDQogIG11dGF0ZShSYW5raW5nID0gcmFuaygtVG90YWwuU3BlbmQsIHRpZXMubWV0aG9kID0gIm1pbiIpLCANCiAgICAgICAgIFBlcmNlbnRpbGUgPVJhbmtpbmcvbWF4KFJhbmtpbmcpKjEwMCwgDQogICAgICAgICBBdmVyYWdlX0ZyYW1lX1NwZW5kID0gVG90YWwuU3BlbmQvRnJhbWVzLkJvdWdodCwgDQogICAgICAgICBBdmVyYWdlX1Zpc2l0X1NwZW5kID0gVG90YWwuU3BlbmQvVmlzaXRzKQ0KDQpyYW5raW5nIDwtIHJhbmtpbmcgJT4lIA0KICBtdXRhdGUocGVyY2VudGlsZV9ncm91cCA9IGN1dChyYW5raW5nJFBlcmNlbnRpbGUsIGJyZWFrcyA9IHNlcSgwLCAxMDAsIDUpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcGVyY2VudGlsZV9sYWJlbCksIA0KICAgICAgICAgYWdlX2dyb3VwID0gY3V0KHJhbmtpbmckQWdlLCBicmVha3MgPSBzZXEoMCwgODUsIDUpLCBsYWJlbHMgPSBBZ2VfbGFiZWwpKQ0KDQp3cml0ZS5jc3YocmFua2luZywgIlJhbmtpbmcuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpoZWFkKHNlbGVjdChyYW5raW5nLCAtYyhOYW1lLCBBZ2UsIFRvdGFsLlNwZW5kLCBBdmVyYWdlX0ZyYW1lX1NwZW5kLCBBdmVyYWdlX1Zpc2l0X1NwZW5kKSksIDkpDQpgYGANCiMgU2FsZXMgd2l0aCBUaW1lDQojIyBpbmRpdmlkdWFsIHNhbGVzIHdpdGggdGltZQ0KYGBge3J9DQojIGdncGxvdGx5KA0KICBnZ3Bsb3QoaW5mb19tZXJnZSAlPiUgZmlsdGVyKEFtb3VudCA+IDApLCBhZXMoeCA9IE9yZGVyLmRhdGUsIHkgPSBBbW91bnQpKSArDQogICAgdGhlbWUoYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpKSArDQogICAgZ2VvbV9wb2ludChzaGFwZSA9IDEpICsNCiAgICBnZW9tX3Ntb290aCgpIywNCiMgICB3aWR0aCA9IDkwMA0KIyApDQpgYGANCg0KIyMgdG90YWwgZGFpbHkgc2FsZXMgd2l0aCB0aW1lDQpgYGB7cn0NClNhbGVzX2J5X2RhdGUgPC0gaW5mb19tZXJnZSAlPiUgDQogICAgICAgICAgICAgICAgICBncm91cF9ieShEYXRlID0gT3JkZXIuZGF0ZSkgJT4lIA0KICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKFNhbGVzID0gc3VtKEFtb3VudCwgbmEucm0gPSBUUlVFKSkNCiMgZ2dwbG90bHkoDQogIGdncGxvdChTYWxlc19ieV9kYXRlLCBhZXMoeCA9IERhdGUsIHkgPSBTYWxlcykpICsNCiAgICB0aGVtZShheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCkpICsNCiAgICBnZW9tX3BvaW50KHNoYXBlID0gMSkgKw0KICAgIGdlb21fc21vb3RoKCkjLA0KIyAgIHdpZHRoID0gOTAwDQojICkNCmBgYA0KDQojIyB0b3RhbCBtb250aGx5IHNhbGVzIHdpdGggdGltZQ0KTW9udGhseSBzYWxlcyBhcHBlYXIgdG8gcmlzZSBhbmQgdGhlbiB0YXBwZXIgb2ZmLCBidXQgdGhlIHRpbWUgZnJhbWUgaXMgc2hvcnQgc28gaXQgbWlnaHQgYmUgYSBibGlwLg0KYGBge3J9DQpTYWxlc19ieV9tb250aCA8LSBpbmZvX21lcmdlICU+JSANCiAgbXV0YXRlKE1vbnRoID0gZmxvb3JfZGF0ZShPcmRlci5kYXRlLCAibW9udGgiKSkgJT4lDQogIGdyb3VwX2J5KE1vbnRoKSAlPiUgDQogIHN1bW1hcmlzZShtb250aF9zYWxlcyA9IHN1bShBbW91bnQsIG5hLnJtID0gVCkpDQoNCiMgU2VwdGVtYmVyIHNhbGVzIGlzIGluY29tcGxldGUgc28gZXhjbHVkZWQgaW4gdGhpcyBjYXNlDQojIGdncGxvdGx5KA0KICBnZ3Bsb3QoU2FsZXNfYnlfbW9udGgsIGFlcyh4ID0gTW9udGgsIHkgPSBtb250aF9zYWxlcywgeW1pbiA9IDApKSArICANCiAgICB0aGVtZShheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCkpICsNCiAgICBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogICAgZ2VvbV9zbW9vdGgoKSMsDQojICAgd2lkdGggPSA5MDANCiMgKQ0KYGBgDQoNCiMjdW5pcXVlIGN1c3RvbWVycyBwbG90IGVhcmxpZXN0IHB1cmNoYXNlIGRhdGUNCkRyb3AgaW4gc2FsZXMgbWlnaHQgYmUgZHVlIHRvIGRyb3AgaW4gbmV3IGN1c3RvbWVyIGFjcXVpc2l0aW9uIHJhdGUuIA0KYGBge3J9DQpuZXdfY3VzdG9tZXIgPC0gaW5mb19tZXJnZSAlPiUgDQogIGZpbHRlcihBbW91bnQgPiAwKSAlPiUNCiAgZ3JvdXBfYnkoTmFtZSkgJT4lDQogIHN1bW1hcmlzZShlYXJsaWVzdC5vcmRlciA9IG1pbihPcmRlci5kYXRlKSkgJT4lDQogIGdyb3VwX2J5KE1vbnRoID0gZmxvb3JfZGF0ZShlYXJsaWVzdC5vcmRlciwgIm1vbnRoIikpICU+JSANCiAgc3VtbWFyaXNlKG5ldy5jdXN0b21lcnMgPSBuKCkpDQoNCnN1bW1hcnkobmV3X2N1c3RvbWVyJG5ldy5jdXN0b21lcnMpDQoNCiMgZ2dwbG90bHkoDQogIGdncGxvdChuZXdfY3VzdG9tZXIsIGFlcyh4ID0gTW9udGgsIHkgPSBuZXcuY3VzdG9tZXJzLCB5bWluID0gMCkpICsNCiAgICB0aGVtZShheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCkpICsNCiAgICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogICAgZ2VvbV9zbW9vdGgoKSArDQogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbihuZXdfY3VzdG9tZXIkbmV3LmN1c3RvbWVycyksIGNvbG9yID0gImdyZWVuIikgKw0KICAgIGdndGl0bGUoIk5ldyBDdXN0b21lciBBY3F1c2l0aW9uIFJhdGUiKSMsDQojICAgd2lkdGggPSA5MDANCiMgKQ0KYGBgDQoNCiMgQ3VzdG9tZXIgbG9jYXRpb24gbWFwcGluZyB7LnRhYnNldH0NCmBgYHtyfQ0KcG9zdF9jb2RlcyA8LSByZWFkLmNzdigiLi9EYXRhL1Bvc3Rjb2Rlcy5jc3YiKQ0KcG9zdF9jb2RlcyA8LSBwb3N0X2NvZGVzWyAsMV0NCmNvb3JkaW5hdGVzIDwtIHJlYWQuY3N2KCJQb3N0Y29kZXMgY29vcmRpbmF0ZXMuY3N2IikNCg0KIyMgaW5zdGFsbCBmb3IgbmV3ZXN0IHZlcmlvbiBvZiBnZ21hcA0KI2lmKCFyZXF1aXJlTmFtZXNwYWNlKCJkZXZ0b29scyIpKSBpbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpDQojZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJka2FobGUvZ2dtYXAiLCByZWYgPSAidGlkeXVwIikNCg0KcmVnaXN0ZXJfZ29vZ2xlKGtleSA9ICIiKQ0KYGBgDQoNCiMjIFVLDQo5MCUgb2YgY3VzdG9tZXJzIGFyZSBsb2NhdGVkIGluIHRoZSBVSw0KYGBge3J9DQpnZ21hcChnZXRfbWFwKCJNYWNoZXN0ZXIiLCB6b29tID0gNiwgYXBpX2tleSA9IGtleSksIGV4dGVudCA9ICJkZXZpY2UiKSArDQogIGdlb21fcG9pbnQoYWVzKHggPSBsb24sIHkgPSBsYXQpLCBkYXRhID0gY29vcmRpbmF0ZXMsDQogICAgICAgICAgICAgYWxwaGEgPSAuMTUsIGNvbG9yPSJkYXJrcmVkIiwgc2l6ZSA9IDIpDQpgYGANCiMjIEdyZWF0ZXIgTG9uZG9uDQo2MCUgb2YgYWxsIGN1c3RvbWVycyBhcmUgbG9jYXRlZCBpbiBMb25kb24NCmBgYHtyfQ0KZ2dtYXAoZ2V0X21hcCgiU1cxVyIsIHpvb20gPSAxMSwgYXBpX2tleSA9IGtleSksIGV4dGVudCA9ICJkZXZpY2UiKSArDQogIGdlb21fcG9pbnQoYWVzKHggPSBsb24sIHkgPSBsYXQpLCBkYXRhID0gY29vcmRpbmF0ZXMsDQogICAgICAgICAgICAgYWxwaGEgPSAuMTUsIGNvbG9yPSJkYXJrcmVkIiwgc2l6ZSA9IDIpDQpgYGANCiMjIExvbmRvbg0KYGBge3J9DQpnZ21hcChnZXRfbWFwKCJTVzFXIiwgem9vbSA9IDEyLCBhcGlfa2V5ID0ga2V5KSwgZXh0ZW50ID0gImRldmljZSIpKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gbG9uLCB5ID0gbGF0KSwgZGF0YSA9IGNvb3JkaW5hdGVzLA0KICAgICAgICAgICAgIGFscGhhID0gLjIsIGNvbG9yPSJkYXJrcmVkIiwgc2l6ZSA9IDIpDQpgYGANCiMjIExvbmRvbiAyDQpgYGB7cn0NCmdnbWFwKGdldF9tYXAoIlNXMVciLCB6b29tID0gMTMsIGFwaV9rZXkgPSBrZXkpLCBleHRlbnQgPSAiZGV2aWNlIikrDQogIGdlb21fcG9pbnQoYWVzKHggPSBsb24sIHkgPSBsYXQpLCBkYXRhID0gY29vcmRpbmF0ZXMsDQogICAgICAgICAgICAgYWxwaGEgPSAuMjUsIGNvbG9yPSJkYXJrcmVkIiwgc2l6ZSA9IDIpDQpgYGANCiMjIFNsb2FuZSBTcXVhcmUNCmBgYHtyfQ0KZ2dtYXAoZ2V0X21hcCgiU2xvYW5lIFNxdWFyZSIsIHpvb20gPSAxNCwgYXBpX2tleSA9IGtleSksIGV4dGVudCA9ICJkZXZpY2UiKSsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGxvbiwgeSA9IGxhdCksIGRhdGEgPSBjb29yZGluYXRlcywNCiAgICAgICAgICAgICBhbHBoYSA9IC4yNSwgY29sb3I9ImRhcmtyZWQiLCBzaXplID0gMikNCmBgYA0KDQojIFNwZW5kaW5nIGJ5IFJlZ2lvbnMNCmBgYHtyfQ0KIyAkVjEgdGFrZXMgaXQgYXMgYSB2ZWN0b3IgaW5zdGVhZCBvZiBkYXRhIGZyYW1lDQpMb25kb25fcmVnaW9ucyA8LSByZWFkLmNzdigiLi9EYXRhL0xvbmRvbiBUb3duIGFuZCBCb3JvdWdocy5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIGhlYWRlciA9IEZBTFNFKSRWMQ0KDQppbmZvX3dhbGsgPC0gcmVhZC5jc3YoIi4vRGF0YS9pbmZvIHdhbGsuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KUG9zdF9jb2RlX3JlZ2V4IDwtICJeKFtBLVBSLVVXWVowLTldW0EtSEstWTAtOV1bQUVITU5QUlRWWFkwLTldP1tBQkVITU5QUlZXWFkwLTldPyB7MSwyfVswLTldW0FCRC1ISkxOLVVXLVpdezJ9fEdJUiAwQUEpJCINCg0KIyBPZiBhbGwgdGhlIGRhdGEgdGhlcmUgYXJlIDMwMCBibGFuayBwb3N0Y29kZXMvYWRkcmVzc2VzIG9mIHdoaWNoIDE2MSBhcmUgcGF5aW5nIGN1c3RvbWVycw0KIyBjb3VudHMgPC0gZGF0YS5mcmFtZShtYXRyaXgobmNvbCA9IDUsIG5yb3cgPSA1KSkNCiMgY291bnRzWywxXSA8LSBjKCJMb25kb24iLCAiVUsiLCAiVUsgZXguIExvbmRvbiIsICJOb24tVUsiLCAiQWxsIikNCiMgY29sbmFtZXMoY291bnRzKSA8LSBjKCJSZWdpb24iLCAiTm8uIGN1c3RvbWVycyIsICJUb3RhbCBTcGVuZCIsICIlIG9mIGN1c3RvbWVycyIsICIlIG9mIHNwZW5kIikNCg0KDQpjb3VudHMgPC0gdGliYmxlKHJlZ2lvbiA9IGMoIkxvbmRvbiIsICJVSyIsICJVSyBleC4gTG9uZG9uIiwgIk5vbi1VSyIsICJBbGwiKSwgDQogICAgICAgICAgICAgICAgIE51bV9DdXN0b21lcnMgPSBOQSwNCiAgICAgICAgICAgICAgICAgVG90YWxfU3BlbmQgPSBOQSwNCiAgICAgICAgICAgICAgICAgUGVyY2VudGFnZWVfQ3VzdG9tZXJzID0gTkEsDQogICAgICAgICAgICAgICAgIFBlcmNlbnRhZ2VfU3BlbmQgPSBOQSkNCg0KVG90YWxfc3BlbmQgPC0gdW5saXN0KGluZm9fd2FsayAlPiUNCiAgZmlsdGVyKEFtb3VudCA+IDAsIFBvc3QgIT0gIiIpICU+JQ0KICBzdW1tYXJpc2Uoc3VtKEFtb3VudCkpKQ0KDQpUb3RhbF9jb3VudCA8LSB1bmxpc3QoaW5mb193YWxrICU+JQ0KICBmaWx0ZXIoQW1vdW50ID4gMCwgUG9zdCAhPSAiIikgJT4lDQogIGRpc3RpbmN0KE5hbWUpICU+JQ0KICBzdW1tYXJpc2UobnVtYmVyID0gbigpKSkNCg0KIyBnZXRzIExvbmRvbiByZXNpZGVudHMgb25seQ0KY291bnRzWzEsIC0xXSA8LSB1bmxpc3QoaW5mb193YWxrICU+JSANCiAgZmlsdGVyKGdyZXBsKFBvc3RfY29kZV9yZWdleCwgUG9zdCksIHRvbG93ZXIoVG93bi54KSAlaW4lIHRvbG93ZXIoTG9uZG9uX3JlZ2lvbnMpLCBBbW91bnQgPiAwKSAlPiUgDQogIGdyb3VwX2J5KE5hbWUpICU+JSANCiAgc3VtbWFyaXNlKFRvdGFsLnNwZW5kID0gc3VtKEFtb3VudCwgbmEucm0gPSBUUlVFKSkgJT4lIA0KICBzdW1tYXJpc2UobnVtYmVyID0gbigpLCBTdW0ub2Yuc3BlbmQgPSByb3VuZChzdW0oVG90YWwuc3BlbmQpKSkgJT4lIA0KICBtdXRhdGUocGVyY2VudC5jb3VudCA9IChudW1iZXIvVG90YWxfY291bnQpKjEwMCwNCiAgICAgICAgIHBlcmNlbnQuc3BlbmQgPSAoU3VtLm9mLnNwZW5kL1RvdGFsX3NwZW5kKSoxMDApKQ0KDQojIGZpbmRzIFVLIHBvc3Rjb2RlcyBvbmx5DQpjb3VudHNbMiwgLTFdIDwtIHVubGlzdChpbmZvX3dhbGsgJT4lIA0KICBmaWx0ZXIoZ3JlcGwoUG9zdF9jb2RlX3JlZ2V4LCBQb3N0KSB8IHRvbG93ZXIoVG93bi54KSAlaW4lIHRvbG93ZXIoTG9uZG9uX3JlZ2lvbnMpLCBBbW91bnQgPiAwKSAlPiUNCiAgZ3JvdXBfYnkoTmFtZSkgJT4lIA0KICBzdW1tYXJpc2UoVG90YWwuc3BlbmQgPSBzdW0oQW1vdW50LCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogIHN1bW1hcmlzZShudW1iZXIgPSBuKCksIFN1bS5vZi5zcGVuZCA9IHJvdW5kKHN1bShUb3RhbC5zcGVuZCkpKSAlPiUNCiAgbXV0YXRlKHBlcmNlbnQuY291bnQgPSAobnVtYmVyL1RvdGFsX2NvdW50KSoxMDAsDQogICAgICAgICBwZXJjZW50LnNwZW5kID0gKFN1bS5vZi5zcGVuZC9Ub3RhbF9zcGVuZCkqMTAwKSkNCg0KIyBmaW5kcyBVSywgbm9uLUxvbmRvbiBwb3N0Y29kZXMNCmNvdW50c1szLCAtMV0gPC0gdW5saXN0KGluZm9fd2FsayAlPiUgDQogIGZpbHRlcihncmVwbChQb3N0X2NvZGVfcmVnZXgsIFBvc3QpLCAhKHRvbG93ZXIoVG93bi54KSAlaW4lIHRvbG93ZXIoTG9uZG9uX3JlZ2lvbnMpKSwgQW1vdW50ID4gMCkgJT4lDQogIGdyb3VwX2J5KE5hbWUpICU+JSANCiAgc3VtbWFyaXNlKFRvdGFsLnNwZW5kID0gc3VtKEFtb3VudCwgbmEucm0gPSBUUlVFKSkgJT4lIA0KICBzdW1tYXJpc2UobnVtYmVyID0gbigpLCBTdW0ub2Yuc3BlbmQgPSByb3VuZChzdW0oVG90YWwuc3BlbmQpKSkgJT4lDQogIG11dGF0ZShwZXJjZW50LmNvdW50ID0gKG51bWJlci9Ub3RhbF9jb3VudCkqMTAwLA0KICAgICAgICAgcGVyY2VudC5zcGVuZCA9IChTdW0ub2Yuc3BlbmQvVG90YWxfc3BlbmQpKjEwMCkpDQoNCiMgZmluZCBub24gVUsgcG9zdGNvZGVzDQpjb3VudHNbNCwgLTFdIDwtIHVubGlzdChpbmZvX3dhbGsgJT4lIA0KICBmaWx0ZXIoIWdyZXBsKFBvc3RfY29kZV9yZWdleCwgUG9zdCksIFBvc3QgIT0gIiIsICEodG9sb3dlcihUb3duLngpICVpbiUgdG9sb3dlcihMb25kb25fcmVnaW9ucykpLCBBbW91bnQgPiAwKSAlPiUNCiAgZ3JvdXBfYnkoTmFtZSkgJT4lIA0KICBzdW1tYXJpc2UoVG90YWwuc3BlbmQgPSBzdW0oQW1vdW50LCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogIHN1bW1hcmlzZShudW1iZXIgPSBuKCksIFN1bS5vZi5zcGVuZCA9IHJvdW5kKHN1bShUb3RhbC5zcGVuZCkpKSAlPiUNCiAgbXV0YXRlKHBlcmNlbnQuY291bnQgPSAobnVtYmVyL1RvdGFsX2NvdW50KSoxMDAsDQogICAgICAgICBwZXJjZW50LnNwZW5kID0gKFN1bS5vZi5zcGVuZC9Ub3RhbF9zcGVuZCkqMTAwKSkNCg0KIyBUaGUgdG90YWxzDQpjb3VudHNbNSwgLTFdIDwtIGMoVG90YWxfY291bnQsIFRvdGFsX3NwZW5kLCAxMDAsIDEwMCkNCg0Kd3JpdGUuY3N2KGNvdW50cywgIi4vQW5hbHlzaXMvQ3VzdG9tZXJzIGJ5IHJlZ2lvbi5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCg0Kc2VsZWN0KGNvdW50cywgLWMoVG90YWxfU3BlbmQsIE51bV9DdXN0b21lcnMpKQ0KYGBgDQoNCiMgV2Fsa2luZyBEaXN0YW5jZSBmcm9tIFN0b3JlDQpgYGB7cn0NCmRpc3RhbmNlIDwtIGRhdGEuZnJhbWUoKQ0KDQojICRWMSB0YWtlcyBpdCBhcyBhIHZlY3RvciBpbnN0ZWFkIG9mIGRhdGEgZnJhbWUNCkxvbmRvbl9yZWdpb25zIDwtIHJlYWQuY3N2KCIuL0RhdGEvTG9uZG9uIFRvd24gYW5kIEJvcm91Z2hzLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwgaGVhZGVyID0gRkFMU0UpJFYxDQpQb3N0X2NvZGVfcmVnZXggPC0gIl4oW0EtUFItVVdZWjAtOV1bQS1ISy1ZMC05XVtBRUhNTlBSVFZYWTAtOV0/W0FCRUhNTlBSVldYWTAtOV0/IHsxLDJ9WzAtOV1bQUJELUhKTE4tVVctWl17Mn18R0lSIDBBQSkkIg0KDQpMb25kb25fY3VzdCA8LSBpbmZvICU+JSANCiAgZmlsdGVyKGdyZXBsKFBvc3RfY29kZV9yZWdleCwgUG9zdCksIHRvbG93ZXIoVG93bikgJWluJSB0b2xvd2VyKExvbmRvbl9yZWdpb25zKSwgQW1vdW50ID4gMCkgJT4lDQogIGRpc3RpbmN0KFBvc3QpDQpgYGANCg0KYGBge3J9DQojIGdldHRpbmcgdGhlIHdhbGtpbmcgZGlzdGFuY2UgZnJvbSBHb29nbGUNCmRpc3RhbmNlIDwtIHVubmVzdCgNCiAgZW5mcmFtZSgNCiAgICBtYXAoTG9uZG9uX2N1c3QkUG9zdCwgbWFwZGlzdCwgdG8gPSAiU1cxVyAxRVgiLCBtb2RlID0gIndhbGtpbmciLCBvdXRwdXQgPSAic2ltcGxlIikNCiAgICApDQogICkNCmluZm9fd2FsayA8LSBpbm5lcl9qb2luKHggPSBpbmZvLCB5ID0gZGlzdGFuY2UsIGJ5ID0gYygiUG9zdCIgPSAiZnJvbSIpKQ0Kd3JpdGUuY3N2KGRpc3RhbmNlLCAiLi9kYXRhL3dhbGtpbmcgZGlzdGFuY2UgZnJvbSBzdG9yZS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCg0Kd2Fsa2luZ19kaXN0IDwtIHJlYWQuY3N2KCIuL2RhdGEvd2Fsa2luZyBkaXN0YW5jZSBmcm9tIHN0b3JlLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCndhbGtpbmdfZGlzdCA8LSBzdWJzZXQod2Fsa2luZ19kaXN0LCBtaW51dGVzIDwgMzAwKQ0KaGVhZChzZWxlY3Qod2Fsa2luZ19kaXN0LCAtZnJvbSkpDQpgYGANCg0KIyMgSGlzdG9ncmFtIG9mIFdhbGtpbmcgRGlzdGFuY2UNCmBgYHtyfQ0KIyBnZ3Bsb3RseSgNCmdncGxvdCh3YWxraW5nX2Rpc3QsIGFlcyhtaW51dGVzKSkgKw0KICB0aGVtZShheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0ocG9zaXRpb24gPSAnc3RhY2snLCBiaW53aWR0aCA9IDUsIGFscGhhID0gMC45LCBjb2wgPSAid2hpdGUiLCBib3VuZGFyeSA9IDApIywNCiMgICB3aWR0aCA9IDkwMA0KIyApDQpgYGANCg0KIyMgQ3VtdWxhdGl2ZSBHcmFwaCBvZiBXYWxraW5nIERpc3RhbmNlIGZyb20gU3RvcmUNCk9ubHkgMzAlIG9mIGN1c3RvbWVycyBsaXZlIHdpdGhpbiBhIDMwIG1pbnV0ZSB3YWxrIGZyb20gdGhlIHN0b3JlLiBTb21lIG1heSB3b3JrIG5lYXJieSwgb3IgdGhlIHN0b3JlIG1heSBiZSBhIGRlc3RpbmF0aW9uIHN0b3JlIGluc3RlYWQgb2YgcmVseWluZyBvbiBsb2NhbCBjbGllbnRlbC4NCmBgYHtyfQ0KZ2dwbG90bHkoDQogIGdncGxvdCh3YWxraW5nX2Rpc3QgJT4lIGFycmFuZ2UobWludXRlcyksIGFlcyhtaW51dGVzKSkgKyANCiAgICBzdGF0X2VjZGYoZ2VvbSA9ICJzdGVwIiwgY29sID0gImJsdWUiLCBzaXplID0gMC41KSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAzMDAsIDE1KSkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMSwgMC4xKSkgKw0KICAgIHlsYWIoIlBlcmNlbnRhZ2UiKSArDQogICAgZ2d0aXRsZSgiV2Fsa2luZyBEaXN0YW5jZSBmcm9tIFN0b3JlIiksDQogIHdpZHRoID0gOTAwDQopDQpgYGANCg0KIyBBZ2UgRGVtb2dyYXBoaWNzIENvbXBhcmlzb24NCmBgYHtyfQ0KIyBDYXRlZ29yaXNlcyBjdXN0b21lciBudW1iZXJzIGFuZCBzcGVuZGluZyBpbnRvIGFnZSBjYXRlZ29yaWVzIGFuZCBmaWx0ZXJzIGJ5IHByb3hpbWl0eSBvZiA0NSBtaW4gd2Fsaw0KYWdlX3N1bW1hcnkgPC0gZnVuY3Rpb24obG93ZXIsIHVwcGVyKXsNCiAgaW5mb193YWxrICU+JSANCiAgICBncm91cF9ieShOYW1lKSAlPiUgDQogICAgc3VtbWFyaXNlKHNwZW5kID0gc3VtKEFtb3VudCwgbmEucm0gPSBUUlVFKSwgQWdlID0gQWdlWzFdLCBtaW51dGVzID0gbWludXRlc1sxXSkgJT4lICAjIGFzIGVhY2ggbmFtZSBoYXMgbXVsdGlwbGUgb25seSB0YWtlIDFzdCBhZ2UmbWluDQogICAgZmlsdGVyKEFnZSA+PSBsb3dlciwgQWdlIDw9IHVwcGVyLCBzcGVuZCA+IDAsIG1pbnV0ZXMgPiAzMCkgJT4lIA0KICAgIHN1bW1hcmlzZShjb3VudCA9IG5fZGlzdGluY3QoTmFtZSkpDQp9DQoNCiMgQ2Vuc3VzIGRhdGENCmNlbnN1cyA8LSByZWFkLmNzdigiLi9DZW5zdXMgZGF0YS9DZW5zdXMgYXJlYSBzdW1tYXJ5LmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCg0KVERfY291bnQgPC0gbWFwcGx5KGFnZV9zdW1tYXJ5LCBsb3dlciA9IGNlbnN1cyRMb3dlciwgdXBwZXIgPSBjZW5zdXMkVXBwZXIpDQpURF9jb3VudCA8LSBtYXRyaXgodW5saXN0KHQoVERfY291bnQpKSwgbmNvbCA9IDEpICAjVHJhbnNwb3NlcyB0aGVuIGNvbnZlcnRzIHRoZSBsaXN0IHRvIGEgbWF0cml4DQpjb2xuYW1lcyhURF9jb3VudCkgPC0gIlREX2NvdW50Ig0KDQpjZW5zdXNfc3VtIDwtIGNiaW5kKGNlbnN1cywgVERfY291bnQpDQpjZW5zdXNfc3VtIDwtIG11dGF0ZShjZW5zdXNfc3VtLCBjb3VudF9wZXJjZW50ID0gVERfY291bnQvc3VtKFREX2NvdW50KSkNCg0KYWdlX2dyb3VwIDwtIHBhc3RlKGNlbnN1c19zdW1bICwxXSwgIi0iLCBjZW5zdXNfc3VtWyAsMl0sIHNlcCA9ICIiKQ0KY29tcGFyaXNvbiA8LSBjYmluZChhZ2VfZ3JvdXAsIGNlbnN1c19zdW1bLDM6Nl0pDQpjb2xuYW1lcyhjb21wYXJpc29uKSA8LSBjKCJhZ2VfZ3JvdXAiLCAiQ291bnQiLCAiQ2Vuc3VzIiwgIlREX2NvdW50IiwgIlREIGN1c3RvbWVycyIpDQpjb21wX2xvbmcgPC0gY2JpbmQoYWdlX2dyb3VwLCBnYXRoZXIoY29tcGFyaXNvblssMjo1XSkpDQpjb21wX2xvbmcgPC0gc3Vic2V0KGNvbXBfbG9uZywga2V5ICVpbiUgYygiQ2Vuc3VzIiwgIlREIGN1c3RvbWVycyIpKQ0KIyBrZWVwIGRhdGEgYXMgZmFjdG9ycywgb3RoZXJ3aXNlIGl0IHNvcnRzIGJ5IG51bWVyaWNhbCBvcmRlcg0KY29tcF9sb25nJGFnZV9ncm91cCA8LSBmYWN0b3IoY29tcF9sb25nJGFnZV9ncm91cCwgbGV2ZWxzID0gdW5pcXVlKGNvbXBfbG9uZyRhZ2VfZ3JvdXApKQ0KY29tcF9sb25nJGtleSA8LSBmYWN0b3IoY29tcF9sb25nJGtleSwgbGV2ZWxzID0gdW5pcXVlKGNvbXBfbG9uZyRrZXkpKQ0KYGBgDQpGb3IgYWdlIGNvbXBhcmlzb24gaXQgb25seSBsb29rcyBhdCAxNjAgb3V0IG9mIDk0MCBjdXN0b21lciwgYWZ0ZXIgZmlsdGVyaW5nIGZvciA0NW1pbiB3YWxraW5nIHByb3hpbWl0eSwgYWdlIGRhdGEgYW5kIHNwZW5kaW5nLg0KT3ZlcmFsbCBjb21wYXJlZCB0byB0aGUgbG9jYWwgYXJlYSB0aGUgc3RvcmUgYXR0cmFjdHMgYSBtdWNoIGxhcmdlciBwcm9wb3J0aW9uIG9mIHRob3NlIGFnZWQgYmV0d2VlbiA0NS01OSBjb21wYXJlZCB0byBvdGhlciBhZ2VzIGdyb3Vwcy4NCmBgYHtyfQ0KY29tcF9sb25nIDwtIGNvbXBfbG9uZyAlPiUgbXV0YXRlKHBlcmNlbnQgPSB2YWx1ZSoxMDApDQojIGdncGxvdGx5KA0KICBnZ3Bsb3QoY29tcF9sb25nLCBhZXMoeCA9IGFnZV9ncm91cCwgeSA9IHBlcmNlbnQsIGZpbGwgPSBrZXkpKSArDQogICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpKSArDQogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoKSkgKw0KICAgIHhsYWIoIkFnZSBDYXRlZ29yaWVzIikgKw0KICAgIHlsYWIoIlBlcmNlbnRhZ2UiKSArDQogICAgZ2d0aXRsZSgiQ29tcGFyaXNvbiBvZiBBZ2UgR3JvdXBzICgyMDExIEsmQyBDZW5zdXMgdnMgU3RvcmUpIikgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCAxMCkpIywNCiMgICB3aWR0aCA9IDkwMCwNCiMgICBoZWlnaHQgPSA1MDANCiMgKQ0KYGBgDQoNCg0K